package com.zte.claa.inficombo.csa.app.comm.csif;

import java.util.Base64;
import java.util.concurrent.atomic.AtomicInteger;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zte.claa.inficombo.csa.app.model.app.AppTarget;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;
import com.zte.claa.inficombo.csa.app.util.XEuiUtils;
import com.zte.claa.inficombo.csa.app.util.ZTEClaaCSChangeKeyGenTool;

public class CSIFCmdBuilder {
	public static final String CHARSET_UTF8 = "utf-8";
	private static Logger logger = LoggerFactory.getLogger(CSIFCmdBuilder.class);
	// 为配合嵌入式link在润和的应用场景，这里seq从10000000开始
	private static int BASE_SEQ = 10000000;
	// 命令序号分配字
	private static AtomicInteger CMD_SEQ = new AtomicInteger(BASE_SEQ);
	// CSIF命令参数名.
	public static final String CMD_NAME = "cmd";
	public static final String CMD_SEQL_NAME = "cmdseq";
	public static final String CODE_NAME = "code";
	public static final String APPEUI_NAME = "appeui";
	public static final String DEVEUI_NAME = "deveui";
	public static final String APPNONCE_NAME = "appnonce";
	public static final String CHALLENGE_NAME = "challenge";
	// 广播可选参数DR & CH
	public static final String BCAST_DR_NAME = "dr";
	public static final String BCAST_CH_NAME = "ch";
	
	// dev-crud para
	public static final String APPKEY_P_NAME = "appkey";
	public static final String CLASS_P_NAME = "class";
	public static final String OFFLINETIME_P_NAME = "offline_time";
	
	// 命令码定义.
	public static final String CMD_HBACK 	= "heartbeat_ack";
	public static final String CMD_JOIN 	= "join";
	public static final String CMD_QUIT 	= "quit";
	public static final String CMD_SENDTO 	= "sendto";
	public static final String CMD_BUFFQUE 	= "buffque";
	public static final String CMD_CLASSQUE = "classque";
	public static final String CMD_BCAST 	= "bcast";
	public static final String CMD_DEVSTATE = "dev_state";
	public static final String CMD_BCASTQUE = "bcastque";
	
	// 新版CSIF接口支持dev的add,del,mod,qry命令. devadd,devdel,devset,devque 
	public static final String CMD_DEVADD 	= "devadd";
	public static final String CMD_DEVDEL 	= "devdel";
	public static final String CMD_DEVSET 	= "devset";
	public static final String CMD_DEVQUE 	= "devque";	
		
	/**
	 * 分配cmdseq
	 * 
	 * @return
	 */
	public static int allocCmdSeq(){
		int cs = CMD_SEQ.addAndGet(1);
		if(cs >= Integer.MAX_VALUE){
			cs = BASE_SEQ;
			CMD_SEQ = new AtomicInteger(BASE_SEQ);
		}
		return cs;
	}
	
	/**
	 * 查询当前最新分配的cmdseq
	 * 
	 * @return
	 */
	public static int getCurrentCmdSeq(){
		return CMD_SEQ.get();
	}
	
	/**
	 * Base64编码 
	 * 
	 * @param data
	 * @return
	 */
	/*
	public static String base64Encode(String data) {
		if (data == null) {
			return "";
		}
		try {
			return Base64.getEncoder().encodeToString(data.getBytes(CHARSET_UTF8));
		} catch (Exception e) {
			logger.error("base64Encode failed: " + data + ", err=" + e.getMessage());
		}
		return "";
	}
	*/
	
	/**
	 * Base64编码 
	 * 
	 * @param data
	 * @return
	 */
	public static String base64Encode(byte[] data) {
		if (data == null) {
			return "";
		}
		try {
			return Base64.getEncoder().encodeToString(data);
		} catch (Exception e) {
			logger.error("base64Encode failed: " +  ", err=" + e.getMessage());
		}
		return "";
	}
	
	/**
	 * Base64解码 （统一UTF-8字符集）
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] base64Decode(String data) {
		if (data == null) {
			return new byte[0];
		}
		try {
			return Base64.getDecoder().decode(data);
		} catch (Exception e) {
			logger.error("base64Decode failed: " + data + ", err=" + e.getMessage());
		}
		return new byte[0];
	}
	
	/**
	 * 0.构造CSIF的心跳响应
	 * 
	 *  {
		  "cmd": "heartbeat_ack"
		}
	 * 
	 * @return
	 */
	public static String buildHeartBeatAck(){
		JSONObject json = new JSONObject();
		try{
			json.put(CMD_NAME, CMD_HBACK);
		}catch(Exception e){
			logger.error("buildHeartBeatAck error: "+e.getMessage(), e);
		}
		return json.toString();
	}
	
	/**
	 * 1. 构造CS注册（auth）命令 
	 * 
	 * {
		    "cmd": "join",
		    "cmdseq": 1,
		    "appeui": "2C26C50001000001",
		    "appnonce": 1234,
		    "challenge": "ABCDEF1234567890ABCDEF1234567890",
		}	
	 * @param appTarget
	 * @return
	 */
	public static CSIFCommand buildCSJoinCmd(final AppTarget appTarget) throws Exception {		
		int nonce = appTarget.getAndIncAppnonce();
		ZTEClaaCSChangeKeyGenTool mac = new ZTEClaaCSChangeKeyGenTool();
		String challenge = mac.genChallengeKey(appTarget.getAppAuthKey(), appTarget.getAppEUI(), nonce);
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_JOIN);
		json.put(CMD_SEQL_NAME, cmdSeq);
		json.put(APPEUI_NAME, XEuiUtils.removeStartZero4xEUI(appTarget.getAppEUI()));
		json.put(APPNONCE_NAME, nonce);
		json.put(CHALLENGE_NAME, challenge);		
		String cmdBody = json.toString();
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_JOIN);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 2. 构造CS退出（quit）命令 
	 * 
	 * {
		    "cmd": "quit", 
		    "cmdseq": 10
		}	
	 * @return
	 */
	public static CSIFCommand buildCSQuitCmd() throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_QUIT);
		json.put(CMD_SEQL_NAME, cmdSeq);		
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_QUIT);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 3. 构造设备控制命令（sendto）命令 
	 * 
		{
		    "cmd": "sendto", 
		    "cmdseq": 5, 
		    "deveui": "2C26C50001FF0001", 
		    "confirm": true,
		    "payload": "xxxxx",
			"port": 21  ----》取值范围在[1~200]有效。
		}	
	 * @return
	 */
	public static CSIFCommand buildDevCtrlCmd(String devEui, boolean confirm, byte[] payload, int port) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_SENDTO);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));
		json.put("confirm", confirm);
		// payload做base64编码.
		json.put("payload", CSIFCmdBuilder.base64Encode(payload));
		if(port > 0 && port <= 200 ){
			json.put("port", port);
		}		
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_SENDTO);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 4. 构造终端下行消息缓存队列查询（buffque）命令 
	 * 
			{
				"cmd": "buffque",
				"cmdseq": 2,
			  	"deveui": "2C26C50001FF0001"
			}
	 * @return
	 */
	public static CSIFCommand buildDevBuffQueryCmd(String devEui) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_BUFFQUE);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));	
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_BUFFQUE);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 5. 构造终端类型查询（classque）命令 
	 * 
		{
			"cmd": "classque",
			"cmdseq": 2,
		  	"deveui": "2C26C50001FF0001"
		}
	 * @return
	 */
	public static CSIFCommand buildDevClassQueryCmd(String devEui) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_CLASSQUE);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));	
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_CLASSQUE);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 6. 构造终端下行广播（bcast）命令 
	 * 
		{
		    "cmd": "bcast", 
		    "cmdseq": 5,
			"payload": "xxxxx",
			"port": 21,
			"times": 5,
			"dr": 5  --- 
		}
	 * @return
	 */
	public static CSIFCommand buildBcastCmd(byte[] payload, int port, int times, int dr, int ch) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_BCAST);
		json.put(CMD_SEQL_NAME, cmdSeq);
		json.put("times", times);
		if(dr >= 0){
			json.put(BCAST_DR_NAME, dr);
		}	
		if(ch >= 0){
			json.put(BCAST_CH_NAME, ch);
		}	
		if(port > 0 && port <= 200 ){
			json.put("port", port);
		}	
		// 对广播净荷做base64编码.
		json.put("payload", CSIFCmdBuilder.base64Encode(payload));
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_BCAST);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 7. 构造终端状态查询（dev_state）命令 
	 * 
			{
				"cmd": "dev_state",
				"cmdseq": 2,
			  	"deveui": "2C26C50001FF0001"
			}
	 * @return
	 */
	public static CSIFCommand buildDevStateQueryCmd(String devEui) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_DEVSTATE);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));	
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_DEVSTATE);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 构造组播密钥查询命令
	 * 
	 *  {
		    "cmd": "bcastque", 
			"cmdseq": 5
		}
	 * 
	 * @param appEui
	 * @return
	 * @throws Exception
	 */
	public static CSIFCommand buildBcastQueueQueryCmd(String appEui) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_BCASTQUE);
		json.put(CMD_SEQL_NAME, cmdSeq);
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_BCASTQUE);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 构造devadd命令
	 * 
	 * {
		"cmd" : "devadd",
		"cmdseq" : 12,
		"deveui" : "2C26C50001FF0001",
		"appkey" : "ABCDEF1234567890ABCDEF1234567890",
		"class" : "B",
		"offline_time" : 24
		}
	 * @param devEui
	 * @return
	 * @throws Exception
	 */
	public static CSIFCommand buildDevAddCmd(String devEui, String appkey, String clazz, int offlineTime) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_DEVADD);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));	
		if(appkey != null && !appkey.equals("")){
			json.put(APPKEY_P_NAME, appkey);
		} 
		if(clazz != null && !clazz.equals("")){
			json.put(CLASS_P_NAME, clazz);
		}
		if(offlineTime > 0) {
			json.put(OFFLINETIME_P_NAME, offlineTime);
		}		
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_DEVADD);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 构造devdel命令
	 * 
	 * {
		"cmd" : "devdel",
		"cmdseq" : 12,
		"deveui" : "2C26C50001FF0001"
		}
	 * @param devEui
	 * @return
	 * @throws Exception
	 */
	public static CSIFCommand buildDevDelCmd(String devEui) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_DEVDEL);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));	
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_DEVDEL);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 构造devset命令
	 * 
	 * {
		"cmd" : "devset",
		"cmdseq" : 12,
		"deveui" : "2C26C50001FF0001",
		"offline_time" : 24
		}
	 * @param devEui
	 * @return
	 * @throws Exception
	 */
	public static CSIFCommand buildDevSetCmd(String devEui, int offlineTime) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_DEVSET);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));	
		if(offlineTime > 0) {
			json.put(OFFLINETIME_P_NAME, offlineTime);
		}		
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_DEVSET);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 构造devque命令
	 * 
	 * {
		"cmd" : "devque",
		"cmdseq" : 12,
		"deveui" : "2C26C50001FF0001"
		}
	 * @param devEui
	 * @return
	 * @throws Exception
	 */
	public static CSIFCommand buildDevQueCmd(String devEui) throws Exception {	
		int cmdSeq = CSIFCmdBuilder.allocCmdSeq();
		JSONObject json = new JSONObject();
		json.put(CMD_NAME, CMD_DEVQUE);
		json.put(CMD_SEQL_NAME, cmdSeq);
		// XEui下发之前去掉开头的0
		json.put(DEVEUI_NAME, XEuiUtils.removeStartZero4xEUI(devEui));			
		String cmdBody = json.toString();		
		CSIFCommand command = new CSIFCommand();
		command.setCmd(CMD_DEVQUE);
		command.setJsonCmdBody(cmdBody);
		command.setCmdseq(cmdSeq);
		return command;
	}
	
	/**
	 * 根据json字符串，解析CSIFResponse对象
	 * 
	 * @param jsonRsp
	 * @return
	 * @throws Exception
	 */
	public static CSIFResponse parseCSIFRsp(String jsonRsp) throws Exception {
		CSIFResponse response = new CSIFResponse();
		JSONObject json = new JSONObject(jsonRsp);
		String cmd = JsonUtils.getJsonAttrVal(json, CMD_NAME, null);
		if(cmd == null){
			throw new Exception("Invalid CSIF rsp (no cmd para): "+jsonRsp);
		}
		String cmdseq = JsonUtils.getJsonAttrVal(json, CMD_SEQL_NAME, null);
		if(cmdseq == null || cmdseq.trim().equals("")){
			throw new Exception("Invalid CSIF rsp (no cmdseq para): "+jsonRsp);
		}
		response.setCmd(cmd);
		response.setJsonRspBody(jsonRsp);
		response.setCmdseq(Integer.parseInt(cmdseq));
		return response;
	}
	
	/**
	 * 根据json字符串，解析CSIFReportData对象
	 * 
	 * @param jsonRpt
	 * @return
	 * @throws Exception
	 */
	public static CSIFReportData parseCSIFReport(String jsonRpt) throws Exception {
		CSIFReportData report = new CSIFReportData();
		JSONObject json = new JSONObject(jsonRpt);
		String cmd = JsonUtils.getJsonAttrVal(json, CMD_NAME, null);
		if(cmd == null){
			throw new Exception("Invalid CSIF rpt (no cmd para): "+jsonRpt);
		}
		report.setCmd(cmd);
		report.setJsonData(jsonRpt);
		return report;
	}
	
}
